package com.filldream.fastboot.core.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.filldream.fastboot.base.entity.DatabaseBean;
import com.filldream.fastboot.base.entity.GenTable;
import com.filldream.fastboot.base.mapper.DatabaseMapper;
import com.filldream.fastboot.base.mapper.GenTableMapper;
import com.filldream.fastboot.base.request.pc.database.DatabaseSTO;
import com.filldream.fastboot.base.request.pc.database.DownCodeSTO;
import com.filldream.fastboot.base.response.pc.database.ColumnInfo;
import com.filldream.fastboot.base.response.pc.database.GenTableColumnDTO;
import com.filldream.fastboot.common.entity.ErrorCode;
import com.filldream.fastboot.common.entity.P;
import com.filldream.fastboot.common.entity.R;
import com.filldream.fastboot.common.exception.GlobalException;
import com.filldream.fastboot.common.util.Assert;
import com.filldream.fastboot.common.util.GenUtil;
import com.filldream.fastboot.core.cache.CacheUtil;
import com.filldream.fastboot.core.service.DatabaseService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author RickSun
 * @version 1.0
 * @data 2021-08-23 17:05
 * @description BannerServiceImpl
 */
@AllArgsConstructor
@Service
@Transactional(rollbackFor = Exception.class)
public class DatabaseServiceImpl extends ServiceImpl<DatabaseMapper, DatabaseBean> implements DatabaseService {

    private DatabaseMapper databaseMapper;

    private GenTableMapper genTableMapper;

    @Override
    public P<DatabaseBean> list( DatabaseSTO databaseSTO ) {
        databaseSTO.initPageStart();
        Long count = databaseMapper.listCount(databaseSTO);
        List<DatabaseBean> databaseBeans = databaseMapper.list(databaseSTO);
        return P.autoSuccess(count,databaseBeans,databaseSTO);
    }

    @Override
    public R<List<ColumnInfo>> column(DatabaseSTO databaseSTO) {
        Assert.isEmpty(databaseSTO.getTableName(),"表名不允许为空");
        List<ColumnInfo> result = databaseMapper.column(databaseSTO);
        return R.autoSuccess(result);
    }

    @Override
    public void downLoadCode(HttpServletResponse response, DownCodeSTO downCodeSTO,String t) throws GlobalException, IOException {
        //校验token
        Assert.isEmpty( CacheUtil.checkPcToken(downCodeSTO.getToken()), ErrorCode.NO_LOGIN.reMsg());
//        DownLoadCodeUtil.gen(response,downCodeSTO,t);
    }

    @Override
    public R<GenTable> tableConfig(DatabaseSTO databaseSTO) {
        GenTable genTable = genTableMapper.selectOne(
                new LambdaQueryWrapper<GenTable>().eq(GenTable::getTableName, databaseSTO.getTableName())
        );
        if( genTable == null ){
            genTable = initTable(databaseSTO);
        }else{
            genTable.initColumnList( );
        }
        return R.successd(genTable);
    }

    @Override
    public R<GenTable> modifyTableConfig(GenTable genTable) {
        GenTable oldTable = getByTableName(genTable.getTableName());
        if( oldTable != null ){
            genTable.setId( oldTable.getId() );
        }
        if(ObjectUtils.isNotEmpty( genTable.getColumnList() )){
            genTable.initColumnJson();
        }
        int i = oldTable == null ? genTableMapper.insert(genTable) : genTableMapper.updateById(genTable);
        return i > 0 ? R.successd( genTable ) : R.fail("修改配置失败");
    }

    public GenTable getByTableName(String tableName){
        GenTable genTable = genTableMapper.selectOne(
                new LambdaQueryWrapper<GenTable>().eq(GenTable::getTableName, tableName)
        );
        return genTable;
    }

    @Override
    public R<Object> codeDetail(String tableName,Integer genType) throws IOException{

        GenTable genTable = getByTableName(tableName);
        Assert.isEmpty(genTable,"尚未配置，请配置后重试~");
        Assert.isBoolean(genTable.getColumnJson().equals("[]"),ErrorCode.BASE_ERR.reMsg("尚未配置字段，请配置后重试~"));

        if( genTable == null ){
            genTable = initTable( new DatabaseSTO().setTableName(tableName) );
        }

        Map<String, Object> genMap = getGenMap(genTable);

        //预览
        if( genType.equals(1) ){
            return R.successd( preview(genMap) );
        }else if( genType.equals(2) ){
            //生成
            return R.successd( gen(genTable,genMap) );
        }
        return R.fail("操作失败~");
    }

    @Override
    public void down(String tableName, HttpServletRequest request, HttpServletResponse response){
        GenTable genTable = getByTableName(tableName);
        Assert.isEmpty(genTable,"尚未配置，请配置后重试~");
        Assert.isBoolean(genTable.getColumnJson().equals("[]"),ErrorCode.BASE_ERR.reMsg("尚未配置字段，请配置后重试~"));
        Map<String, Object> genMap = getGenMap(genTable);

        try {
            File file = new File(getDownloadPath( genTable,genMap ));
            String zipPath = file.getPath() + ".zip";
            ZipUtil.zip(file.getPath(), zipPath);
            GenUtil.downloadFile(request, response, new File(zipPath), true);
        } catch (IOException e) {
            throw new GlobalException("打包失败");
        }
    }

    @Override
    public R<GenTable> refresh(String tableName) {
        GenTable genTable = getByTableName(tableName);
        DatabaseSTO databaseSTO = new DatabaseSTO().setTableName(tableName);
        if( genTable == null ){
            genTable = initTable( databaseSTO  );
            return R.successd( genTable );
        }else{
            R<List<ColumnInfo>> columnInfoR = this.column(databaseSTO);
            List<ColumnInfo> columnInfoList = columnInfoR.getData();
            genTable.initColumnList();
            List<GenTableColumnDTO> oldTableColumnList = genTable.getColumnList();
            List<GenTableColumnDTO> newTableColumnList = new ArrayList<>();

            for (ColumnInfo item : columnInfoList) {
                String columnName = item.getColumnName();
                List<GenTableColumnDTO> genTableColumns = oldTableColumnList.stream().filter(t -> t.getFieldName().equals(columnName)).collect(Collectors.toList());
                if( ObjectUtils.isNotEmpty( genTableColumns ) ){
                    GenTableColumnDTO itemTableColumn = genTableColumns.get(0);
                    itemTableColumn.setColumnComment( item.getColumnComment() );
                    //判断类型是否一致
                    if( !itemTableColumn.getFieldType().contains(item.getDataType()) ){
                        itemTableColumn.setFieldType(  item.getColumnType() );
                        itemTableColumn.setFieldJavaType( GenUtil.cloToJava( item.getDataType() ) );
                    }
                    newTableColumnList.add(itemTableColumn);
                }else{
                    GenTableColumnDTO itemTableColumn = new GenTableColumnDTO();
                    itemTableColumn.setFieldName( item.getColumnName() );
                    itemTableColumn.setFieldType(item.getColumnType());
                    itemTableColumn.setRequiredFlag(!item.getCanNull());
                    itemTableColumn.setFieldRemark(  item.getColumnComment() ) ;
                    itemTableColumn.setFieldJavaName(  StrUtil.toCamelCase( item.getColumnName() ) );
                    itemTableColumn.setFieldJavaType(GenUtil.cloToJava( item.getDataType() ) );
                    itemTableColumn.setAddFlag( !item.getCanNull() );
                    itemTableColumn.setUpdateFlag( !item.getCanNull() );
                    itemTableColumn.setColumnComment( item.getColumnComment() );
                    //主键编辑需要，添加不需要
                    if( item.getColumnKey().equals(GenUtil.PK) ){
                        itemTableColumn.setUpdateFlag( true );
                        itemTableColumn.setAddFlag( false );
                    }
                    itemTableColumn.setFieldKeyType( item.getColumnKey() );
                    itemTableColumn.setExtra( item.getExtra() );
                    newTableColumnList.add(itemTableColumn);
                }
            }
            genTable.setTableName( databaseSTO.getTableName() );
            genTable.initColumnJson( newTableColumnList );
            genTable.setColumnList( newTableColumnList );
            genTable.setClassName( StrUtil.upperFirst(  StrUtil.toCamelCase(databaseSTO.getTableName()) ) );
            return R.successd(genTable);
        }
    }

    public String getDownloadPath(GenTable genTable,Map<String, Object> genMap) throws IOException{
        String tempPath = GenUtil.SYS_TEM_DIR + "fastbootGenTemp" + File.separator + genTable.getTableName() + File.separator;
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
        // 生成后端代码
        List<String> templates = GenUtil.getJavaTemps();
        for (String templateName : templates) {
            Template template = engine.getTemplate("generator/admin/" + templateName + ".ftl");
            String filePath = GenUtil.getJavaFilePath(templateName,genTable.getProjectPrefix(), genTable.getPackageName(), genMap.get("className").toString(), tempPath + genTable.getProjectPrefix());
            assert filePath != null;
            File file = new File(filePath);

            // 生成代码
            GenUtil.genFile(file, template, genMap);
        }


        templates = GenUtil.getVueTemps();
        for (String templateName : templates) {
            Template template = engine.getTemplate("generator/front/" + templateName + ".ftl");
            String rootPath = GenUtil.SYS_TEM_DIR + "fastbootGenTemp" + File.separator + genTable.getTableName() + File.separator;
            String filePath = GenUtil.getVuePath(templateName,genTable.getVuePath(),genTable.getClassName(),  rootPath + "vue");

            assert filePath != null;
            File file = new File(filePath);

            //如果非覆盖生成
            if (!genTable.getCoverFlag() && FileUtil.exist(file)) {
                continue;
            }
            // 生成代码
            GenUtil.genFile(file, template, genMap);
        }

        return tempPath;
    }


    public R<Object> gen(GenTable genTable,Map<String, Object> genMap) throws IOException{
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
        // 生成后端代码
        List<String> templates = GenUtil.getJavaTemps();
        for (String templateName : templates) {
            Template template = engine.getTemplate("generator/admin/" + templateName + ".ftl");
            String rootPath = System.getProperty("user.dir");
            String filePath = GenUtil.getJavaFilePath(templateName,genTable.getProjectPrefix(), genTable.getPackageName(), genMap.get("className").toString(), rootPath);

            assert filePath != null;
            File file = new File(filePath);

            //如果非覆盖生成
            if (!genTable.getCoverFlag() && FileUtil.exist(file)) {
                continue;
            }
            // 生成代码
            GenUtil.genFile(file, template, genMap);
        }

        templates = GenUtil.getVueTemps();
        for (String templateName : templates) {
            Template template = engine.getTemplate("generator/front/" + templateName + ".ftl");
            String rootPath = GenUtil.SYS_TEM_DIR + "fastbootGenTemp" + File.separator + genTable.getTableName() + File.separator;
            String filePath = GenUtil.getVuePath(templateName,genTable.getVuePath(),genTable.getClassName(),  rootPath + "vue");

            assert filePath != null;
            File file = new File(filePath);

            //如果非覆盖生成
            if (!genTable.getCoverFlag() && FileUtil.exist(file)) {
                continue;
            }
            // 生成代码
            GenUtil.genFile(file, template, genMap);
        }

        return null;
    }


    public static List<Map<String, Object>> preview(Map<String, Object> genMap) {
        List<Map<String, Object>> genList = new ArrayList<>();
        //获取前端模版
        List<String> templates = GenUtil.getJavaTemps();
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
        for (String templateName : templates) {
            Map<String, Object> map = new HashMap<>(1);
            Template template = engine.getTemplate("generator/admin/" + templateName + ".ftl");
            map.put("content", template.render(genMap));
            map.put("name", templateName);
            genList.add(map);
        }


        //获取前端模版
        templates = GenUtil.getVueTemps();
        for (String templateName : templates) {
            Map<String, Object> map = new HashMap<>(1);
            Template template = engine.getTemplate("generator/front/" + templateName + ".ftl");
            map.put("content", template.render(genMap));
            map.put("name", templateName);
            genList.add(map);
        }
        return genList;
    }



    //获取模版数据
    private static Map<String, Object> getGenMap(GenTable genConfig) {

        genConfig.initColumnList();

        //存储模版字段数据
        Map<String, Object> genMap = new HashMap<>(32);

        //功能名称
        genMap.put("functionName", genConfig.getFunctionName());
        //包名称
        genMap.put("package", genConfig.getPackageName());
        //模块名称
        genMap.put("moduleName", StrUtil.lowerFirst( genConfig.getClassName() ));
        genMap.put("tableComment",  genConfig.getTableComment() );
        //作者
        genMap.put("author", genConfig.getFunctionAuthor());
        //创建日期
        genMap.put("date", LocalDate.now().toString());
        //表名
        genMap.put("tableName", genConfig.getTableName());
        //Java类名
        genMap.put("className", genConfig.getClassName());
        //保存小写开头的类名
        genMap.put("lowerClassName", StrUtil.lowerFirst( genConfig.getClassName() )  );
        //存在Date-年月日字段
        genMap.put("hasDate", false);
        //存在 BigDecimal 字段
        genMap.put("hasBigDecimal", false);
        //存在Date-年月日时分秒字段
        genMap.put("hasDateTime", false);
        genMap.put("hasD", false);
        //自增主键
        genMap.put("auto", false);
        //存在字典
        genMap.put("hasDict", false);
        //是否存在逻辑删除字段
        genMap.put("delFlag", false);
        genMap.put("vueClass", genConfig.getVuePath().replaceAll("/","__"));


        //所有字段信息
        List<Map<String, Object>> columns = new ArrayList<>();
        //插入的字段信息
        List<Map<String, Object>> addColumns = new ArrayList<>();
        //编辑的字段信息
        List<Map<String, Object>> updateColumns = new ArrayList<>();
        //编辑或保存的字段信息
        List<Map<String, Object>> addOrUpdateColumns = new ArrayList<>();
        //查询字段的信息
        List<Map<String, Object>> queryColumns = new ArrayList<>();
        //列表字段的信息
        List<Map<String, Object>> listColumns = new ArrayList<>();


        //循环遍历每个字段  listMap表示每个字段的信息 genMap表示总体配置信息
        for (GenTableColumnDTO column : genConfig.getColumnList()) {
            Map<String, Object> listMap = new HashMap<>(32);

            //--------------listMap字段配置信息-----------------
            //字段描述
            listMap.put("fieldRemark", column.getFieldRemark());
            listMap.put("columnComment", column.getColumnComment());
            //字段数据库类型
            listMap.put("fieldKeyType", column.getFieldKeyType());
            listMap.put("fieldType", column.getFieldType());
            //大写开头的Java字段名
            String upperColumnName = StrUtil.upperFirst( column.getFieldJavaName() );
            listMap.put("upperColumnName", upperColumnName);
            //小写开头的Java字段名
            listMap.put("lowerColumnName", column.getFieldJavaName());
            //Java字段类型
            String fieldJavaType = column.getFieldJavaType();
            listMap.put("fieldJavaType", fieldJavaType);
            //存储字原始段名称
            listMap.put("fieldName", column.getFieldName());
            //是否不为空
            listMap.put("requiredFlag", column.getRequiredFlag());
            //是否列表字段
            listMap.put("listFlag", column.getListFlag());
            //是否新增字段
            listMap.put("addFlag", column.getAddFlag());
            //是否编辑字段
            listMap.put("updateFlag", column.getUpdateFlag());
            //是否查询字段
            listMap.put("queryFlag", column.getQueryFlag());
            //查询类型
            listMap.put("fieldQueryType", column.getFieldQueryType());
            //显示类型 文本框，文本域，下拉框，单选框，日期框，复选框，图片，文件
            listMap.put("showType", StringUtils.isNotBlank(column.getShowType()) ? column.getShowType() : "Input");
            //字典名称
            listMap.put("dictType", column.getDictType());
            listMap.put("hasDate", GenUtil.DATE.equals(fieldJavaType) && column.getFieldType().equals("date"));
            listMap.put("hasDateTime", GenUtil.DATE.equals(fieldJavaType) && column.getFieldType().equals("datetime"));
            listMap.put("hasD", GenUtil.DATE.equals(fieldJavaType));
            listMap.put("hasBigDecimal", GenUtil.BIGDECIMAL.equals(fieldJavaType));
            listMap.put("hasDict", StringUtils.isNotBlank(column.getDictType()));
            listMap.put("isBetween", "BETWEEN".equals(column.getFieldQueryType()));
            //查询方式 =，!=，>，>=，<，<=，Like，BETWEEN
            listMap.put("fieldQueryType", column.getFieldQueryType());


            //--------------genMap整体配置信息-----------------
            //如果是主键
            if (  GenUtil.PK.equals(column.getFieldKeyType()))   {
                // 存储主键类型
                genMap.put("pkColumnType", fieldJavaType);
                //存储小写开头的Java字段名
                genMap.put("pkLowerColName", column.getFieldJavaName());
                //存储大写开头的Java字段名
                genMap.put("pkUpperColName", upperColumnName);
            }
            if( genMap.get("delFlag").equals(false) ){
                genMap.put( "delFlag" ,column.getFieldName().equals("del_flag")  );
                genMap.put( "delFlagFieldJavaType" ,column.getFieldJavaType()  );
            }
            if( genMap.get("hasDate").equals(false) ){
                genMap.put( "hasDate" ,listMap.get("hasDate") );
            }
            if( genMap.get("hasDateTime").equals(false) ){
                genMap.put( "hasDateTime" ,listMap.get("hasDateTime") );
            }
            if( genMap.get("hasBigDecimal").equals(false) ){
                genMap.put( "hasBigDecimal" ,listMap.get("hasBigDecimal") );
            }
            if( genMap.get("auto").equals(false) ){
                genMap.put( "auto" ,GenUtil.EXTRA.equals(column.getFieldKeyType()) );
            }


            //保存每个类型的list
            if ( column.getQueryFlag() ) {
                queryColumns.add(listMap);
            }
            if( (column.getAddFlag() || column.getUpdateFlag()) && !column.getFieldKeyType().equals(GenUtil.PK)){
                addOrUpdateColumns.add(listMap);
            }
            if( column.getAddFlag() ){
                addColumns.add(listMap);
            }
            if( column.getUpdateFlag() ){
                updateColumns.add(listMap);
            }
            if( column.getListFlag() ){
                listColumns.add(listMap);
            }
            columns.add(listMap);
        } //end list

        genMap.put("columns", columns);
        genMap.put("queryColumns", queryColumns);
        genMap.put("addColumns", addColumns);
        genMap.put("updateColumns", updateColumns);
        genMap.put("listColumns", listColumns);
        genMap.put("addOrUpdateColumns", addOrUpdateColumns);

        System.out.println("qc-------------》" + JSONUtil.toJsonStr(queryColumns));
        return genMap;
    }


    //若没有配置的话则按此规则默认生成临时配置
    public GenTable initTable(DatabaseSTO databaseSTO){
        R<List<ColumnInfo>> r = this.column(databaseSTO);
        List<ColumnInfo> data = r.getData();

        List<GenTableColumnDTO> columnList = new ArrayList<>();
        for (ColumnInfo item : data) {
            GenTableColumnDTO itemColumn = new GenTableColumnDTO();
            itemColumn.setFieldName( item.getColumnName() );
            itemColumn.setFieldType(item.getColumnType());
            itemColumn.setRequiredFlag(!item.getCanNull());
            itemColumn.setFieldRemark(  item.getColumnComment() ) ;
            itemColumn.setFieldJavaName(  StrUtil.toCamelCase( item.getColumnName() ) );
            itemColumn.setFieldJavaType(GenUtil.cloToJava( item.getDataType() ) );
            itemColumn.setAddFlag( !item.getCanNull() );
            itemColumn.setUpdateFlag( !item.getCanNull() );
            itemColumn.setColumnComment( item.getColumnComment() );
            //主键编辑需要，添加不需要
            if( item.getColumnKey().equals(GenUtil.PK) ){
                itemColumn.setUpdateFlag( true );
                itemColumn.setAddFlag( false );
            }

            itemColumn.setFieldKeyType( item.getColumnKey() );
            itemColumn.setExtra( item.getExtra() );

            columnList.add(itemColumn);
        }

        GenTable genTable = new GenTable();
        genTable.setTableName( databaseSTO.getTableName() );
        genTable.initColumnJson( columnList );
        genTable.setColumnList( columnList );
        genTable.setClassName( StrUtil.upperFirst(  StrUtil.toCamelCase(databaseSTO.getTableName()) ) );
        return genTable;
    }







}
